/*****************************************************************************/
/* CS 230 Final Project                                                      */
/* events.cpp   			                                                 */
/*                                                                           */
/* Created by Even Cheng and Shu-Chi Hsu                                     */
/* Copyright 2011 GNU GPL v3                                                 */
/*****************************************************************************/

#include "stdafx.h"
#include <iostream>
#include <time.h>
#include <Windows.h>
#include "events.h"
using namespace std;

events::events(void)
{
	int i = 0;
	int node;
	int flag;
	element* index = NULL;
	element* check = NULL;

	head = NULL;
	srand((unsigned int)(time(NULL)));
	for(i=0;i<NUM_OF_NODES_UP_AND_DOWN-1;i++)
	{
		flag = 0;
		while(flag == 0)
		{
			node = rand() % MAX_NODE_NUMBER;
			check = head;
			while(check != NULL)
			{
				if(check->nodeID == node)
					break;
				else
					check = check->next;	
			}
			if(check == NULL && node != 0)
				flag = 1;
			if(flag == 1)
			{
				if(head == NULL)
				{
					head = new element;
					head->nodeID = node;
					head->clock = rand()%(MAX_RUNNING_TIME/2)+1;
					head->on = 0;
					head->eventType = EventType::NODE_EVENT;

					head->next = new element;
					head->next->nodeID = node;
					head->next->clock = head->clock + rand()%(MAX_RUNNING_TIME/2)+1;
					index = head->next;
					index->on = 1;
					index->next = NULL;
					index->eventType = EventType::NODE_EVENT;

					tail = index;
				}
				else
				{
					index->next = new element;
					index = index->next;
					index->nodeID = node;
					index->clock = rand()%(MAX_RUNNING_TIME/2);
					index->on = 0;
					index->eventType = EventType::NODE_EVENT;

					index->next = new element;
					index->next->nodeID = node;
					index->next->clock = index->clock + rand()%(MAX_RUNNING_TIME/2)+1;
					index = index->next;
					index->on = 1;
					index->next = NULL;
					index->eventType = EventType::NODE_EVENT;

					tail = index;
				}
			}
		}
	}

	// initialize a message event so we can start from that
	index->next = new element;
	index = index->next;

	index->clock = 50; // rand() % 100 + 1;
	index->nodeID = 1;
	index->eventType = EventType::RECV_EVENT;
	index->next = NULL;
	index->on = 1;

	index->message = new messageInfo;
	index->message->messageID = 1;
	index->message->messageTimestamp = 50;
	index->message->senderID = 1;
	index->message->channelNumber = 4;		// always start with human contact
	index->message->recipientID = 1;

	tail = index;
	nodesReceivedMessage = 0;
}

events::~events(void)
{
	thisGraph.~graph();
}

void events::sort()
{
	int record;
	//element *prev;
	element *index;
	element *large;
	element *temp = NULL;

	while(head != NULL)
	{
		record = head->clock;
		large = head;
		index = head;
		while(index->next != NULL)
		{
			if(index->next->clock > record)
			{
				record = index->next->clock;
				large = index;
			}
			else if(index->next->clock == record)
			{
				if(index->next->nodeID < large->next->nodeID)
				{
					record = index->next->clock;
					large = index;
				}
			}
			index = index->next;
		}
		if(temp == NULL)
		{
			temp = large->next;
			large->next = large->next->next;
			temp->next = NULL;
		}
		else
		{
			if(large == head && large->clock == record)
			{
				index = large;
				head = head->next;
				index->next = temp;
				temp = index;
			}
			else
			{
				index = large->next;
				large->next = large->next->next;
				index->next = temp;
				temp = index;
			}
		}
	}
	head = temp;
}

int events::simulate(graph *sim, int t, int *thisNodeID)
{
	element *index;
	neighbor *indexNeighbor;
	int flag = -1;
	//int sendMessageThreshold;
	int sendTime, recvTime;
	node senderNode, recipientNode;

	//if(head == NULL){		// no more event is queued
	//	cout << "no more event is queued!" << endl;
	//	return -2;
	//}

	if(t >= head->clock)
	{
		flag = 1;
		switch(head->eventType){
		case EventType::NODE_EVENT:
			if(head->on == 1)
			{
				cout << "time " << t << ": " << head->nodeID << " joins" << endl;
				flag = 1;
			}
			if(head->on == 0)
			{
				cout << "time " << t << ": " << head->nodeID << " leaves" << endl;
				flag = 0;
			}
			break;
		case EventType::SEND_EVENT:
			senderNode = sim->getNode(head->nodeID - 1);
			indexNeighbor = senderNode.recipientList;
			if(senderNode.on){
				senderNode.message->messageTimestamp = t;
				//senderNode.message->messageID = global::messageID;

				for(int i = 0; i < senderNode.neighbor_number; i++){
					
					if(sim->wantsToSendMessage(senderNode, rand() % 100 / (float)100)){

						
						recipientNode = sim->getNode(indexNeighbor->nodeID);
						if(recipientNode.nodeStatus == NONE)
							recipientNode.nodeStatus = RECEIVING;
						
						element *recvEvent = new element;
						recvEvent->eventType = RECV_EVENT;
						recvEvent->message = new messageInfo;
						recvEvent->message->senderID = senderNode.ID;
						recvEvent->message->recipientID = recipientNode.ID;
						
						// if they are close enough, they would choose to use human contact first,
						// else, the node will base on its channel preference and make a choice
						if(sim->getDistance(senderNode, recipientNode) < HUMAN_CONTACT_DISTANCE)
							recvEvent->message->channelNumber = 4;
						else
							recvEvent->message->channelNumber = sim->getChannelUsed(senderNode, rand() % 100 / (float) 100);

						recvEvent->message->messageID = senderNode.message->messageID;
						recvEvent->message->hasRead = false; 
					
						recvTime = senderNode.message->messageTimestamp + 
							sim->getSendDelay(senderNode.message->channelNumber, senderNode) + 
							sim->getTransmissionDelay(recvEvent->message->channelNumber, senderNode, recipientNode);

#if DEBUG
						cout << "time " << t << ": send: (" << senderNode.ID << ") --> " << 
							senderNode.recipientList->nodeID << " via channel " << recvEvent->message->channelNumber <<
							", should arrive at time: " << recvTime << endl;
#endif
						recvEvent->clock = recvTime;
						recvEvent->message->messageTimestamp = recvTime;

						recvEvent->nodeID = recipientNode.ID;
						recvEvent->on = 1;
						recvEvent->next = NULL;

						recipientNode.message = recvEvent->message;
						sim->setNode(recipientNode);

						insertEventAtTimeT(recvTime, recvEvent);
					
						
					}
					if(indexNeighbor->next != NULL)		// keep shifting to next neighbor if there is one
						indexNeighbor = indexNeighbor->next;
				}
				senderNode.nodeStatus = SENT;
				sim->setNode(senderNode);

			}else{		// if senderNode is offline: sender will skip this send event and 
#if DEBUG
				cout << "time " << t << ": send: (" << senderNode.ID << ") --> " << 
					senderNode.recipientList->nodeID << ": " <<
					" sender is not online, send event skipped" << endl;
#endif
				//recvTime = t + rand() % 100;			// if it's offline, we postpone the sending event
				//index = head;
				//index->clock = recvTime;
				//index->message->messageTimestamp = recvTime;
				//insertEventAtTimeT(recvTime, index);

				senderNode.nodeStatus = RESENDABLE;
				sim->setNode(senderNode);
			}
			break;

		// in recv event it's much simpler. we grab the message from queue to our virtual holder when it arrives,
		// and add with recv delay defined by each user, and incoporate the message into the send event 
		// and queue it into our event queue.
		case EventType::RECV_EVENT:
			if(head->nodeID == 0)		// not exist such node. nodeID starts from 1
				break;
			
			recipientNode = sim->getNode(head->nodeID - 1);

			if(recipientNode.nodeStatus < RECEIVED/* ||
				recipientNode.nodeStatus == RESENDABLE*/){	// if the node in RECEIVED, SENDING, or SENT state,
															// then we don't do anything on it (it guarantees it has recv the msg)
				if(recipientNode.on){
					
					sendTime = t + 
						sim->getRecvDelay(head->message->channelNumber, recipientNode);
			
					if(recipientNode.message != NULL &&						// remove event if the message is not exist
						recipientNode.message->messageTimestamp > 0 &&		// remove redundent message if it won't be able to make it recv earlier
						recipientNode.message->messageTimestamp > sendTime){
						break;
					}
					//recipientNode.message = head->message;
					//if(recipientNode.nodeStatus < RECEIVED){

					// in this case it has received the message before,
					// so we won't increase the statistic number
					//if(recipientNode.nodeStatus != RESENDABLE)	 
					if(recipientNode.nodeStatus != RESENDABLE)
						nodesReceivedMessage = nodesReceivedMessage + 1;
					/*else
						nodesReceivedMessage = nodesReceivedMessage - 1;*/

					element *sendEvent = new element;
					sendEvent->clock = sendTime;
					sendEvent->eventType = SEND_EVENT;
					sendEvent->message = new messageInfo;
					sendEvent->message->senderID = recipientNode.ID;
					sendEvent->message->messageTimestamp = sendTime;
					sendEvent->message->messageID = recipientNode.message->messageID;
					sendEvent->nodeID = recipientNode.ID;
					sendEvent->on = 1;
					sendEvent->next = NULL;
					insertEventAtTimeT(sendTime, sendEvent);
					//delete(sendEvent);

						

					recipientNode.message->hasRead = true;
					recipientNode.nodeStatus = RECEIVED;
					sim->setNode(recipientNode);

#if DEBUG
					printf("time %d : recv: %d --> (%d) via channel %d. send again at time: %d",
						t, recipientNode.message->senderID, recipientNode.ID, recipientNode.message->channelNumber, sendTime);

					cout << "time " << t << ": recv: " << recipientNode.message->senderID << " --> (" << 
							recipientNode.ID << ") via channel " << recipientNode.message->channelNumber <<
						". send again at time: " << sendTime << endl;

					cout << "***" << nodesReceivedMessage << "***" << endl;
#endif
				}else{		// if the recipient is not online, we will sent to his/her mail inbox
					cout << "time " << t << ": recv: node" << 
							recipientNode.ID <<
							". recipient is not online." << endl;
					recipientNode.message = head->message;
					recipientNode.message->hasRead = false;
					recipientNode.nodeStatus = NONE;
					sim->setNode(recipientNode);
				}
			}
			break;
		}
		*thisNodeID = head->nodeID;
		index = head;
		head = head->next;
		free(index);
	}

	return flag;
}

bool events::insertEventAtTimeT(int thisTime, element *targetElement)
{
	element *index = head;

	if(index->clock > targetElement->clock){
		targetElement->next = index;
		head = targetElement;
	}else{
		do{
			if(index->next == NULL){
				index->next = targetElement;
				break;
			}
			if(index->next->clock > targetElement->clock){
				targetElement->next = index->next;
				index->next = targetElement;
				break;
			}else{
				index = index->next;
			}
		}while(true);
	}
	return true;
}

bool events::removeHead(int thisTime, element *targetElement)
{
	free(head);
	head = targetElement;
	return true;
}

void events::info()
{
	element *index = head;

	while(index != NULL)
	{
		cout << "ID: " << index->nodeID << endl;
		cout << "Clock: " << index->clock << endl;
		cout << "On: " << index->on << endl; 
		cout << endl;
		index = index->next;
	}
}